package com.tuling.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NioSelectorServer {

    private static Selector selector;

    public static void main(String[] args) {
        try {
            // 创建NIO serverSocketChannel
            ServerSocketChannel serverSocket = ServerSocketChannel.open();
            serverSocket.socket().bind(new InetSocketAddress("127.0.0.1", 9999));
            // 设置ServerSocketChannel为非阻塞
            serverSocket.configureBlocking(false);
            // 打开Selector处理Channel,即创建epoll
            selector = Selector.open();
            // 把ServerSocketChannel注册到selector上，并且selector对客户端accept连接操作感兴趣
            SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务启动成功:" + selectionKey + "注册上去");

            while (true) {
                // 阻塞等待需要处理的事件发生
                int select = selector.select();
                System.out.println("select = " + select);

                // 获取selector中注册的全部事件的selectionKey实例
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                // 遍历selectKey
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    // 如果是OP_ACCEPT事件，则进行连接获取和事件注册
                    if (key.isAcceptable()) {
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = server.accept();
                        socketChannel.configureBlocking(false);
                        // 这里只注册了读事件，如果需要给客户发送数据可以注册写事件
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        System.out.println("客户端连接成功");
                        // 注册写事件
                        socketChannel.register(selector,SelectionKey.OP_WRITE);
                    } else if (key.isReadable()) { //如果是OP_READ事件，则进行读取和打印
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                        int len = socketChannel.read(byteBuffer);
                        // 如果有数据，把数据打印出来
                        if (len > 0) {
                            System.out.println("接收到消息:" + new String(byteBuffer.array()));
                        } else if (len == -1) {
                            System.out.println("客户端断开连接");
                            socketChannel.close();
                        }
                        // 处理输出
                        handleOut(socketChannel);
                    } else if (key.isWritable()){
                        SocketChannel sc = (SocketChannel)key.channel();
                        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
                        if (byteBuffer.hasRemaining()) {
                            int count = sc.write(byteBuffer);
                            System.out.println("write:"+count+" remain:"+byteBuffer.remaining());
                        } else {
                            // 取消对写的注册
                            key.interestOps(SelectionKey.OP_READ);
                        }
                    }
                    // 从事件集合里删除本次处理的key，防止下次select重复处理
                    iterator.remove();
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void handleOut(SocketChannel socketChannel) throws ClosedChannelException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(128);
        byteBuffer.put("hello,whh".getBytes());
        byteBuffer.flip();
        socketChannel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
    }
}
